package wxopen_api

import (
	"context"
	"fmt"
	"net/http"
)

// MPService Wechat API docs: https://developers.weixin.qq.com/doc/
type MPService service

// ActionInfo 场景信息
type ActionInfo struct {
	// 场景值ID 临时二维码时为32位非0整型，永久二维码时最大值为100000（目前参数只支持1--100000）
	SceneID int32 `json:"scene_id,omitempty"`
	// 场景值ID（字符串形式的ID），字符串类型，长度限制为1到64
	SceneStr string `json:"scene_str,omitempty"`
}

// CreateQRCodeTicketRequest represents a request to create qr_code ticket.
type CreateQRCodeTicketRequest struct {
	// 二维码类型：
	//	   QR_SCENE为临时的整型参数值
	//	   QR_STR_SCENE为临时的字符串参数值
	//	   QR_LIMIT_SCENE为永久的整型参数值
	//	   QR_LIMIT_STR_SCENE为永久的字符串参数值
	ActionName string `json:"action_name,omitempty"`
	// 二维码详细信息
	ActionInfo *ActionInfo `json:"action_info,omitempty"`
	// 二维码有效时间，以秒为单位。 最大不超过2592000（即30天），此字段如果不填，则默认有效期为30秒。
	ExpireSeconds int64 `json:"expire_seconds,omitempty"`
}

// CreateQRCodeTicketResponse represents a ticket.
type CreateQRCodeTicketResponse struct {
	Ticket        string `json:"ticket"`
	ExpireSeconds int64  `json:"expire_seconds"`
	URL           string `json:"url"`
}

// CreateQRCodeTicket create new qr_code ticket.
//
// Wechat API docs:
// https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html
func (s *MPService) CreateQRCodeTicket(ctx context.Context, token string, r *CreateQRCodeTicketRequest) (*CreateQRCodeTicketResponse, *Response, error) {
	u := fmt.Sprintf("cgi-bin/qrcode/create?access_token=%v", token)
	req, err := s.client.NewRequest(http.MethodPost, u, r)
	if err != nil {
		return nil, nil, err
	}
	ticket := new(CreateQRCodeTicketResponse)
	resp, err := s.client.Do(ctx, req, ticket)
	if err != nil {
		return nil, resp, err
	}
	return ticket, resp, nil
}

// GetAccessTokenRequest get a access_token
type GetAccessTokenRequest struct {
	GrantType string `json:"grant_type"`
	AppID     string `json:"appid"`
	Secret    string `json:"secret"`
}

// GetAccessTokenResponse get a access_token response.
type GetAccessTokenResponse struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int64  `json:"expires_in"`
}

// GetAccessToken get a access_token
func (s *MPService) GetAccessToken(ctx context.Context, appID, secret string) (*GetAccessTokenResponse, *Response, error) {
	u := fmt.Sprintf("cgi-bin/token?grant_type=client_credential&appid=%v&secret=%v", appID, secret)
	req, err := s.client.NewRequest(http.MethodGet, u, nil)
	if err != nil {
		return nil, nil, err
	}
	token := new(GetAccessTokenResponse)
	resp, err := s.client.Do(ctx, req, token)
	if err != nil {
		return nil, resp, err
	}
	return token, resp, nil
}

// GetUserInfoResponse 获取用户信息回复
type GetUserInfoResponse struct {
	//用户是否订阅该公众号标识，值为0时，代表此用户没有关注该公众号，拉取不到其余信息。
	Subscribe int32 `json:"subscribe,omitempty"`
	// 用户的标识，对当前公众号唯一
	OpenID string `json:"openid,omitempty"`
	// 用户的昵称
	Nickname string `json:"nickname,omitempty"`
	// 用户的性别，值为1时是男性，值为2时是女性，值为0时是未知
	Sex int32 `json:"sex,omitempty"`
	// 用户所在城市
	City string `json:"city,omitempty"`
	// 用户所在国家
	Country string `json:"country,omitempty"`
	// 用户所在省份
	Province string `json:"province,omitempty"`
	// 用户的语言，简体中文为zh_CN
	Language string `json:"language,omitempty"`
	// 用户头像，最后一个数值代表正方形头像大小（有0、46、64、96、132数值可选，0代表640*640正方形头像），用户没有头像时该项为空。若用户更换头像，原有头像URL将失效。
	Avatar string `json:"headimgurl,omitempty"`
	// 用户关注时间，为时间戳。如果用户曾多次关注，则取最后关注时间
	SubscribeTime int64 `json:"subscribe_time,omitempty"`
	//	只有在用户将公众号绑定到微信开放平台帐号后，才会出现该字段。
	UnionID string `json:"unionid,omitempty"`
	// 公众号运营者对粉丝的备注，公众号运营者可在微信公众平台用户管理界面对粉丝添加备注
	Remark string `json:"remark,omitempty"`
	// 用户所在的分组ID（兼容旧的用户分组接口）
	GroupID int64 `json:"groupid,omitempty"`
	// 用户被打上的标签ID列表
	Tags []int64 `json:"tagid_list,omitempty"`
	// 返回用户关注的渠道来源
	// ADD_SCENE_SEARCH 公众号搜索
	// ADD_SCENE_ACCOUNT_MIGRATION 公众号迁移
	// ADD_SCENE_PROFILE_CARD 名片分享
	// ADD_SCENE_QR_CODE 扫描二维码
	// ADD_SCENE_PROFILE_LINK 图文页内名称点击
	// ADD_SCENE_PROFILE_ITEM 图文页右上角菜单
	// ADD_SCENE_PAID 支付后关注
	// ADD_SCENE_WECHAT_ADVERTISEMENT 微信广告
	// ADD_SCENE_OTHERS 其他
	SubscribeScene string `json:"subscribe_scene,omitempty"`
	// 二维码扫码场景（开发者自定义）
	QrScene int32 `json:"qr_scene,omitempty"`
	// 二维码扫码场景描述（开发者自定义）
	QrSceneStr string `json:"qr_scene_str,omitempty"`
}

// GetUserInfo 获取用户信息
func (s *MPService) GetUserInfo(ctx context.Context, accessToken, openID, lang string) (*GetUserInfoResponse, error) {
	u := fmt.Sprintf("cgi-bin/user/info?access_token=%v&openid=%v&lang=%v", accessToken, openID, lang)
	req, err := s.client.NewRequest(http.MethodGet, u, nil)
	if err != nil {
		return nil, err
	}
	resp := new(GetUserInfoResponse)
	_, err = s.client.Do(ctx, req, resp)
	if err != nil {
		return resp, err
	}
	return resp, nil
}

// Text 文本消息
type Text struct {
	// 内容
	Content string `json:"content,omitempty"`
}

// Image 图片消息
type Image struct {
	// 媒体文件ID
	MediaID string `json:"media_id,omitempty"`
}

// Voice 语音消息
type Voice struct {
	// 媒体文件ID
	MediaID string `json:"media_id,omitempty"`
}

// Video 视频消息
type Video struct {
	// 媒体文件ID
	MediaID string `json:"media_id,omitempty"`
	// 封面文件媒体ID
	ThumbMediaID string `json:"thumb_media_id,omitempty"`
	// 标题
	Title string `json:"title,omitempty"`
	// 描述
	Description string `json:"description,omitempty"`
}

// Music 音乐消息
type Music struct {
	// 音乐标题
	Title string `json:"title,omitempty"`
	// 音乐描述
	Description string `json:"description,omitempty"`
	// 音乐URL
	MusicURL string `json:"musicurl,omitempty"`
	// hq 音乐URL
	HQMusicURL string `json:"hqmusicurl,omitempty"`
	// 封面文件媒体ID
	ThumbMediaID string `json:"thumb_media_id,omitempty"`
}

// News 图文消息
type News struct {
	Articles []*Article `json:"articles,omitempty"`
}

// MPNews 图文消息
type MPNews struct {
	// 媒体文件ID
	MediaID string `json:"media_id,omitempty"`
}

// MenuContent 菜单内容
type MenuContent struct {
	// ID
	ID string `json:"id,omitempty"`
	// 内容
	Content string `json:"content,omitempty"`
}

// MsgMenu 菜单消息
type MsgMenu struct {
	// 头部内容
	HeadContent string `json:"head_content,omitempty"`
	// 菜单内容列表
	MenuList []*MenuContent `json:"list,omitempty"`
	// 尾部内容
	TailContent string `json:"tail_content,omitempty"`
}

// WXCard 卡券消息
type WXCard struct {
	// 卡券ID
	CardID string `json:"card_id,omitempty"`
}

// MiniProgramPage 小程序消息
type MiniProgramPage struct {
	// 标题
	Title string `json:"title,omitempty"`
	// 小程序app_id
	AppID string `json:"appid,omitempty"`
	// 页面路径
	PagePath string `json:"pagepath,omitempty"`
	// 媒体文件ID
	ThumbMediaID string `json:"thumb_media_id,omitempty"`
}

// Article 文章
type Article struct {
	// 标题
	Title string `json:"title,omitempty"`
	// 描述
	Description string `json:"description,omitempty"`
	// URL
	URL string `json:"url,omitempty"`
	// 照片URL
	PicURL string `json:"picurl,omitempty"`
}

// SendCustomMessageRequest 发送客服消息
type SendCustomMessageRequest struct {
	// 用户open_id
	ToUser string `json:"touser,omitempty"`
	// 消息类型: text
	MsgType string `json:"msgtype,omitempty"`
	// 文本消息
	Text *Text `json:"text,omitempty"`
	// 图片消息
	Image *Image `json:"image,omitempty"`
	// 语音消息
	Voice *Voice `json:"voice,omitempty"`
	// 视频消息
	Video *Video `json:"video,omitempty"`
	// 音乐消息
	Music *Music `json:"music,omitempty"`
	// 图文(外链)消息
	News *News `json:"news,omitempty"`
	// 图文消息
	MPNews *MPNews `json:"mp_news,omitempty"`
	// 菜单消息
	MsgMenu *MsgMenu `json:"msgmenu,omitempty"`
	// 卡券
	WxCard *WXCard `json:"wxcard,omitempty"`
	// 小程序卡片（要求小程序与公众号已关联）
	MiniProgramPage *MiniProgramPage `json:"miniprogrampage,omitempty"`
}

// SendCustomMessage 发送自定义客服消息
func (s *MPService) SendCustomMessage(ctx context.Context, accessToken string, r *SendCustomMessageRequest) error {
	u := fmt.Sprintf("cgi-bin/message/custom/send?access_token=%v", accessToken)
	req, err := s.client.NewRequest(http.MethodPost, u, r)
	if err != nil {
		return err
	}
	_, err = s.client.Do(ctx, req, nil)
	if err != nil {
		return err
	}
	return nil
}

// OauthResponse Oauth 返回
type OauthResponse struct {
	OpenID string `json:"openid,omitempty"`
}

// Oauth Oauth
func (s *MPService) Oauth(ctx context.Context, appID, secret string, code string) (*OauthResponse, error) {
	u := fmt.Sprintf("sns/oauth2/access_token?appid=%v&secret=%v&code=%v&grant_type=authorization_code", appID, secret, code)
	req, err := s.client.NewRequest(http.MethodGet, u, nil)
	if err != nil {
		return nil, err
	}
	resp := new(OauthResponse)
	_, err = s.client.Do(ctx, req, resp)
	if err != nil {
		return nil, err
	}
	return resp, err
}

// Ticket 凭据
type Ticket struct {
	Ticket    string `json:"ticket,omitempty"`
	ExpiresIn int64  `json:"expires_in,omitempty"`
}

// GetTicket 获取Ticket
func (s *MPService) GetTicket(ctx context.Context, token string, appType string) (*Ticket, error) {
	u := fmt.Sprintf("cgi-bin/ticket/getticket?access_token=%v&type=%v", token, appType)
	req, err := s.client.NewRequest(http.MethodGet, u, nil)
	if err != nil {
		return nil, err
	}
	resp := new(Ticket)
	_, err = s.client.Do(ctx, req, resp)
	if err != nil {
		return nil, err
	}
	return resp, err
}
